package com.ke.jun.view.widght

import android.animation.PropertyValuesHolder
import android.animation.TimeInterpolator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.ke.jun.view.utils.dp2px
import com.ke.jun.view.utils.sp2px
import java.util.*
import kotlin.math.ceil
import kotlin.math.min


/**
 * 小米时钟的实现
 */
class MIClockView @JvmOverloads constructor(
    context: Context, attributeSet: AttributeSet?=null, defStyleAttr:Int=0): View(context,attributeSet,defStyleAttr) {
    //画边缘线的画笔
    private val mPaintOutCircle=Paint()
    //画边缘的文字
    private val mPaintOutText=Paint()
    //画进度条
    private val mPaintProgressBg=Paint()
    private val mPaintProgress=Paint()
    //画三角
    private val mPaintTriangle=Paint()
    //画时钟
    private val mPaintHour=Paint()
    //画分钟
    private val mPiantMinute=Paint()
    //画中间小球
    private val mPaintBall=Paint()
    //半透明的白色
    private val color_halfWhite: Int = Color.argb(255 - 180, 255, 255, 255)
    //纯白色
    private val color_white: Int = Color.parseColor("#FFFFFF")
    private val color_ball: Int =Color.parseColor("#4169E1")
    //宽高
    private var mWidth:Int=0
    private var mHeigth:Int=0
    //中心坐标
    private var mCenterX:Int=0
    private var mCenterY:Int=0
    private  val paddingOut=dp2px(25f)
    private  val innerRadius=dp2px(6f)
    private var mHourDegress=0
    private var mMinuteDegress=0
    private var mSecondMillsDegress:Float=0f
    private var mSecondDegress=0
    //时钟的半径不包含paddding值

    /* 时钟半径，不包括padding值 */
    private var mRadius: Float = 0.toFloat()
    /* 手指松开时时钟晃动的动画 */
    private var mShakeAnim: ValueAnimator? = null
    /* 触摸时作用在Camera的矩阵 */
    private val mCameraMatrix: Matrix by lazy { Matrix() }
    /* 照相机，用于旋转时钟实现3D效果 */
    private val mCamera: Camera by lazy { Camera() }
    /* camera绕X轴旋转的角度 */
    private var mCameraRotateX: Float = 0f
    /* camera绕Y轴旋转的角度 */
    private var mCameraRotateY: Float = 0f
    /* camera旋转的最大角度 */
    private val mMaxCameraRotate = 10f
    /* 指针的在x轴的位移 */
    private var mCanvasTranslateX: Float = 0f
    /* 指针的在y轴的位移 */
    private var mCanvasTranslateY: Float = 0f
    /* 指针的最大位移 */
    private var mMaxCanvasTranslate: Float = 0f
    /* 画布 */
    private lateinit var mCanvas: Canvas


    init {
        //画外圈的圆形
        mPaintOutCircle.apply {
            color=color_halfWhite
            strokeWidth=dp2px(1f)
            style=Paint.Style.STROKE
        }


        //画外圈的圆形
        mPaintOutText.apply {
            color=color_halfWhite
            strokeWidth=dp2px(1f)
            style=Paint.Style.STROKE
            textSize=sp2px(11f)
            textAlign=Paint.Align.CENTER
        }

        //进度的背景
        mPaintProgressBg.apply {
            color=color_halfWhite
            strokeWidth=dp2px(2f)
            style=Paint.Style.STROKE
        }

        //进度画笔
        mPaintProgress.apply {
            color=color_halfWhite
            strokeWidth=dp2px(2f)
            style= Paint.Style.STROKE
        }


        //画三角形的
        mPaintTriangle.apply {
            color=color_white
            style= Paint.Style.FILL
        }

        //小时的画笔
        mPaintHour.apply {
            color=color_white
            style=Paint.Style.FILL
        }

        //分钟的画笔
        mPiantMinute.apply {
            color=color_white
            strokeWidth=dp2px(3f)
            style=Paint.Style.STROKE
            strokeCap=Paint.Cap.ROUND
        }
        mPaintBall.apply {
            color=color_ball
            style=Paint.Style.FILL
        }




    }

     //绘制的方法
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
         mCanvas=canvas
         //平移到试图中心 计算都是对称的
         canvas.translate(mCenterX.toFloat(), mCenterY.toFloat())
         //操作3D效果的时候必须在绘制之前否则无效
         setCameraRotate()//todo
         drawArcCircle(canvas)//画外卷的大圆
         drawOutText(canvas)//画 3、6 9、12的文字
         drawCalibrationLine(canvas)//画秒钟的刻度
         drawSecond(canvas)//画秒钟的三角给渐变的刻度
         drawMinute(canvas)//画分钟
         drawHour(canvas)//画时针
         drawBall(canvas)


    }

    /**
     * 绘制中间的小球
     */
    private fun drawBall(canvas: Canvas) {
        canvas.drawCircle(0f, 0f, innerRadius / 2, mPaintBall)
    }

    /**
     * 画时钟
     */

    private fun drawHour(canvas: Canvas) {
         canvas.save()
         canvas.rotate(mHourDegress.toFloat())
        canvas.drawCircle(0f,0f,innerRadius,mPaintTriangle)
        val path = Path()
        path.moveTo(-innerRadius / 2, 0f)
        path.lineTo(innerRadius / 2, 0f)
        path.lineTo(innerRadius / 6, -(width / 4).toFloat())
        path.lineTo(-innerRadius / 6, -(width / 4).toFloat())
        path.close()
        canvas.drawPath(path, mPaintHour)
        canvas.restore()

    }

    private fun drawMinute(canvas: Canvas) {
        canvas.save()
        canvas.rotate(mMinuteDegress.toFloat())//时钟旋转的角度
        canvas.drawLine(0f,0f,0f,-(width/3).toFloat(),mPiantMinute)
    }

    /**
     * 绘制秒
     * 这个是最有难度的一个地方，三角形通过旋转画布实现，知道秒表走的角度就可以了
     */
    private fun drawSecond(canvas: Canvas) {
        canvas.save()
        val path=Path()
        canvas.rotate(mSecondMillsDegress)
        path.moveTo(0f, -width * 3f / 8 + dp2px(5f))
        path.lineTo(dp2px(8f), -width * 3f / 8 + dp2px(20f))
        path.lineTo(-dp2px(8f), -width * 3f / 8 + dp2px(20f))
        path.close()
        canvas.drawPath(path, mPaintTriangle)
        canvas.restore()

        //绘制渐变的刻度
        val min=width.coerceAtMost(mHeigth)/2
        for ( i in 0 ..90 step 2){
            canvas.save()
            mPaintProgress.setARGB((255-2.7*i).toInt(),255,255,255,)
            //这里的先减去90°，是为了旋转到开始角度，因为开始角度是y轴的负方向
            canvas.rotate((mSecondDegress-90-i).toFloat())
            canvas.drawLine(min.toFloat()*3/4,0f,min*3/4 +dp2px(10f),0f,mPaintProgress)
            canvas.restore()
        }


    }


    /**
     * 绘制刻度线
     * desc:每个2°绘制一个刻度，也就是有180个刻度
     */
    private fun drawCalibrationLine(canvas: Canvas) {
        val min= width.coerceAtMost(mHeigth)/2
        for (i in 0 until 360 step 2){
            canvas.save()
            canvas.rotate(i.toFloat())
            canvas.drawLine(min.toFloat()*3/4,0f,min*3/4+dp2px(10f),0f,mPaintProgressBg)
            canvas.restore()
        }

    }

    /**
     * 然后绘制4个文字刻度：3,6,9,12
     */
    private fun drawOutText(canvas: Canvas) {
        val min= width.coerceAtMost(mHeigth)
        val textRadius=(min-paddingOut)/2
        val fm = mPaintOutText.fontMetrics
        //文字的高度
        val mTxtHeight= ceil((fm.leading-fm.ascent).toDouble()).toInt()
        canvas.drawText("3", textRadius, (mTxtHeight / 2).toFloat(), mPaintOutText)
        canvas.drawText("9", -textRadius, (mTxtHeight / 2).toFloat(), mPaintOutText)
        canvas.drawText("6", 0f, textRadius + mTxtHeight / 2, mPaintOutText)
        canvas.drawText("12", 0f, -textRadius + mTxtHeight / 2, mPaintOutText)

    }

    /**
     * 首先画外边缘的弧度，分成四个弧度，假设每个弧度中间的间隔是6个弧度
     * 那么弧度的位置是 5-85,95-175,185 -265,275-355；
     */

    private fun drawArcCircle(canvas: Canvas) {
        val min=if (width>mHeigth) mHeigth else width
        val rect = RectF(-(min - paddingOut) / 2, -(min - paddingOut) / 2, (min - paddingOut) / 2, (min - paddingOut) / 2)
        canvas.drawArc(rect, 5f, 80f, false, mPaintOutCircle)
        canvas.drawArc(rect, 95f, 80f, false, mPaintOutCircle)
        canvas.drawArc(rect, 185f, 80f, false, mPaintOutCircle)
        canvas.drawArc(rect, 275f, 80f, false, mPaintOutCircle)
    }

    /**
     * 设置3D时钟效果，触摸矩阵的相关设置、照相机的旋转大小
     * 应用在绘制图形之前，否则无效
     * desc:Camera的坐标系是左手坐标系。当手机平整的放在桌面上，X轴是手机的水平方向，Y轴是手机的竖直方向，Z轴是垂直于手机向里的那个方向。
     * Camera内部机制实际上还是opengl，不过大大简化了使用。
     */
    private fun setCameraRotate() {
        mCameraMatrix.reset()
        mCamera.save()
        mCamera.rotateX(mCameraRotateX)//绕x轴旋转角度
        mCamera.rotateY(mCameraRotateY)//绕y轴旋转角度
        mCamera.getMatrix(mCameraMatrix)//相关属性设置到matrix中
        mCamera.restore()
        mCanvas.concat(mCameraMatrix)//matrix 与canvas建立绑定关系

    }

    /**
     * 当尺寸发生变化的时候调佣
     */
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        mWidth=w
        mHeigth=h
        mCenterX=mWidth/2
        mCenterY=mHeigth/2
        mRadius=(((w - paddingLeft - paddingRight).coerceAtMost(h - paddingTop - paddingBottom) /2).toFloat())
        mMaxCanvasTranslate= (0.02*mRadius).toFloat()

    }
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        //正方形
        val widthSize  = MeasureSpec.getSize(widthMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        val imageSize=if (widthSize>heightSize) widthSize else heightSize
        setMeasuredDimension(imageSize,imageSize)
    }


    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        startTick()
    }
    // 指针转动的方法
    private fun startTick() {
        postDelayed(mRunnable,150)
    }

    private val mRunnable = Runnable {
        calcuateDegree()
        invalidate()
        startTick()
    }

    private fun calcuateDegree() {
        val mCalendar = Calendar.getInstance()
        mCalendar.timeInMillis=System.currentTimeMillis()
        val minute=mCalendar.get(Calendar.MINUTE)
        val secondMills=mCalendar.get(Calendar.MILLISECOND)
        val second=mCalendar.get(Calendar.SECOND)
        val hour=mCalendar.get(Calendar.HOUR)
        mHourDegress=hour*30
        mMinuteDegress= minute*6
        mSecondMillsDegress= second*6+secondMills*0.006f
        mSecondDegress=second*6
        //因为是每2°旋转一个刻度，所以这里要根据毫秒值来进行计算
        when (secondMills*0.006f.toInt()) {
             in 2 until 4 -> {
                mSecondDegress += 2
            }
            in 4 until 6 -> {
                mSecondDegress += 4
            }
        }


    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when(event.action){
            MotionEvent.ACTION_DOWN->{
                mShakeAnim?.let {
                    it.cancel()
                }
                getCameraRotate(event)
                getCanvasTranslate(event)
            }

            MotionEvent.ACTION_MOVE -> {
                //根据手指坐标计算camera应该旋转的大小
                getCameraRotate(event)
                getCanvasTranslate(event)
            }
            MotionEvent.ACTION_UP ->
                //松开手指，时钟复原并伴随晃动动画
                startShakeAnim()
        }



        return true
    }

    /**
     * 时钟晃动动画
     */
    private fun startShakeAnim() {
        val cameraRotateXName = "cameraRotateX"
        val cameraRotateYName = "cameraRotateY"
        val canvasTranslateXName = "canvasTranslateX"
        val canvasTranslateYName = "canvasTranslateY"
        val cameraRotateXHolder = PropertyValuesHolder.ofFloat(cameraRotateXName, mCameraRotateX, 0f)
        val cameraRotateYHolder = PropertyValuesHolder.ofFloat(cameraRotateYName, mCameraRotateY, 0f)
        val canvasTranslateXHolder = PropertyValuesHolder.ofFloat(canvasTranslateXName, mCanvasTranslateX, 0f)
        val canvasTranslateYHolder = PropertyValuesHolder.ofFloat(canvasTranslateYName, mCanvasTranslateY, 0f)
        mShakeAnim = ValueAnimator.ofPropertyValuesHolder(cameraRotateXHolder,
            cameraRotateYHolder, canvasTranslateXHolder, canvasTranslateYHolder)
        mShakeAnim?.interpolator = TimeInterpolator { input ->
            //http://inloop.github.io/interpolator/
            val f = 0.571429f
            (Math.pow(2.0, (-2 * input).toDouble()) * Math.sin((input - f / 4) * (2 * Math.PI) / f) + 1).toFloat()
        }
        mShakeAnim?.duration = 1000
        mShakeAnim?.addUpdateListener { animation ->
            mCameraRotateX = animation.getAnimatedValue(cameraRotateXName) as Float
            mCameraRotateY = animation.getAnimatedValue(cameraRotateYName) as Float
            mCanvasTranslateX = animation.getAnimatedValue(canvasTranslateXName) as Float
            mCanvasTranslateY = animation.getAnimatedValue(canvasTranslateYName) as Float
        }
        mShakeAnim?.start()
    }

    /**
     * 当拨动时钟时，会发现时针、分针、秒针和刻度盘会有一个较小的偏移量，形成近大远小的立体偏移效果
     * 一开始我打算使用 matrix 和 camera 的 mCamera.translate(x, y, z) 方法改变 z 的值
     */
    private fun getCanvasTranslate(event: MotionEvent) {
        val translateX = event.x - width / 2
        val translateY = event.y - height / 2
        //求出此时位移的大小与半径之比
        val percentArr = getPercent(translateX, translateY)
        //最终位移的大小按比例匀称改变
        mCanvasTranslateX = percentArr[0] * mMaxCanvasTranslate
        mCanvasTranslateY = percentArr[1] * mMaxCanvasTranslate
    }

    /**
     * 获取camera旋转的大小
     *
     * @param event motionEvent
     */

    private fun getCameraRotate(event: MotionEvent) {
       val rotateX=-(event.y-height/2)
       val rotatey=-(event.x-width/2)
        //求出此时旋转的大小与半径的比例
        val percentArr=getPercent(rotateX,rotationY)
        //最终旋转的大小按比例匀称改变
        mCameraRotateX = percentArr[0] * mMaxCameraRotate
        mCameraRotateY = percentArr[1] * mMaxCameraRotate
    }

    private fun getPercent(x: Float, y: Float): FloatArray {
        val percentArr = FloatArray(2)
        var percentX = x / mRadius
        var percentY = y / mRadius
        if (percentX > 1) {
            percentX = 1f
        } else if (percentX < -1) {
            percentX = -1f
        }
        if (percentY > 1) {
            percentY = 1f
        } else if (percentY < -1) {
            percentY = -1f
        }
        percentArr[0] = percentX
        percentArr[1] = percentY
        return percentArr
    }


    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        removeCallbacks(mRunnable)
    }


    }